/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#include "zip.h"
#include "log.h"

#include "zipheaders.h"

#ifdef _DEBUG
#ifdef _MSC_VER
   #define new new( _CLIENT_BLOCK, __FILE__, __LINE__)
#endif
#endif // _DEBUG

namespace Engine
{
	using namespace std;
	/**********************************************************************************************************/
	Zip* ZipFactory::CreateInstance( const std::string& name )
	{
		return new Zip(name);
	}
	/**********************************************************************************************************/
	Zip::Zip(const std::string& name)
	{
		zzip_error_t zzipError;
		m_ZzipDir = zzip_dir_open(name.c_str(), &zzipError);
		if(zzipError != ZZIP_NO_ERROR)
		{
			Log::GetSingleton() << "ERROR: can't open zip \"" << name << "\"" <<endl;
			throw invalid_argument("Bad zip archive " + name);
		}
	}
	/**********************************************************************************************************/
	Zip::~Zip()
	{
		if (m_ZzipDir)
		{
			zzip_dir_close(m_ZzipDir);
			m_ZzipDir = 0;
		}
	}
	/**********************************************************************************************************/
	RefPtr<DataStream> Zip::ReadFile(const std::string& name)const
	{
		ZZIP_FILE* zzipFile = zzip_file_open(m_ZzipDir, name.c_str(), ZZIP_ONLYZIP | ZZIP_CASELESS);
		if (!zzipFile)
		{
			Log::GetSingleton() << "ERROR: " << m_name + " - Unable to open file " + name << endl;
			return RefPtr<DataStream>();
		}

		// Get uncompressed size too
		ZZIP_STAT zstat;
		zzip_dir_stat(m_ZzipDir, name.c_str(), &zstat, ZZIP_CASEINSENSITIVE);

		// Construct & return stream
		return RefPtr<DataStream>(new ZipDataStream(zzipFile, static_cast<size_t>(zstat.st_size)));
	}
	/**********************************************************************************************************/
	Archive::VectorStringPtr Zip::Find(const std::string& pattern, bool recursive, bool dirs)const
	{
		Archive::VectorStringPtr ret(new vector<string>);

		ZZIP_DIRENT zzipEntry;
		while (zzip_dir_read(m_ZzipDir, &zzipEntry))
		{
			string entry(zzipEntry.d_name);

			bool bDir = entry.find("/") == (entry.length() - 1);

			if(entry.find(pattern) != entry.npos)
			{
				if(dirs == bDir)
					ret->push_back(entry);
				if(!recursive)
					break;
			}

		}

        return ret;
	}
	/**********************************************************************************************************/

	/**********************************************************************************************************/
	ZipDataStream::ZipDataStream(ZZIP_FILE* zzipFile, size_t uncompressedSize):
		m_file(zzipFile),
		m_size(uncompressedSize)
	{
	}
	/**********************************************************************************************************/
	ZipDataStream::~ZipDataStream()
	{
		Close();
	}
	/**********************************************************************************************************/
	size_t ZipDataStream::Read(void* buf, size_t count)
	{
		 return zzip_file_read(m_file, (char*)buf, count);
	}
	/**********************************************************************************************************/
	void ZipDataStream::Seek( size_t pos )
	{
		zzip_seek(m_file, static_cast<zzip_off_t>(pos), SEEK_SET);
	}
	/**********************************************************************************************************/
	size_t ZipDataStream::Tell(void) const
	{
		return zzip_tell(m_file);
	}
	/**********************************************************************************************************/
	bool ZipDataStream::Eof(void) const
	{
		return (zzip_tell(m_file) >= static_cast<zzip_off_t>(m_size));
	}
	/**********************************************************************************************************/
	void ZipDataStream::Close(void)
	{
		zzip_file_close(m_file);
	}
	/**********************************************************************************************************/
	size_t ZipDataStream::GetSize(void)
	{
		return m_size;
	}
	/**********************************************************************************************************/
}
